package xiangyi.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.util.StringUtils;
import xiangyi.entity.Employee;
import xiangyi.entity.EmployeeHours;
import xiangyi.entity.EmployeeSalaryDetails;
import xiangyi.service.IExcelReaderService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Project Name: Tmall_demo-master
 * Package Name: com.xq.tmall.service.impl
 * File Name: ExcelReaderServiceImpl
 * 2022/9/9 20:17 - 李宗宝
 */
@Service
public class ExcelReaderServiceImpl implements IExcelReaderService {

    protected Logger logger = LogManager.getLogger(ExcelReaderServiceImpl.class);

    @Override
    public ExcelWriter uploadExcel(MultipartFile file) {

        try {

            //创建输入流
            InputStream inputStream = file.getInputStream();
            Workbook workbook = null;
            try {
                //获得文件的后缀名
                String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
                //根据后缀名判断
                if (suffix.equals(".xls")) {
                    //Excel 2003
                    workbook = new HSSFWorkbook(inputStream);
                } else if (suffix.equals(".xlsx")) {
                    //Excel 2007
                    workbook = new XSSFWorkbook(inputStream);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
            Sheet sheet = workbook.getSheetAt(0);
            List<Employee> employeeList = new ArrayList<>();
            //员工类
            Employee employee = null;
            //考勤日期
            String attendanceDate = "";
            //考勤日期
            String tabulationTime = "";
            System.out.println("总行数" + (sheet.getLastRowNum() + 1));
            //按行读取数据
            for (int i = 1; i < sheet.getLastRowNum() + 1; i++) {
                Row row = sheet.getRow(i);

                //记录考勤日期
                if (row.getCell(25).toString().contains("考勤日期")) {
                    String[] attendanceStrs = row.getCell(25).toString().split("：");
                    attendanceDate = attendanceStrs[1];
                }

                //记录制表时间
                if (row.getCell(25).toString().contains("制表时间")) {
                    String[] tabulationTStrs = row.getCell(25).toString().split("：");
                    tabulationTime = tabulationTStrs[1];
                }

                //如果该行是工号行，则新建员工 "工号：".equals(row.getCell(4).toString())
                if (isNewEmployeeRow(row)) {
                    //如果当前行进入前，有员工，则加入队列后再新建
                    if (employee != null) {
                        employeeList.add(employee);
                    }

                    //读取员工信息
                    employee = getEmployeeByEmployeeRow(row);
//                    //读取工号
//                    employee.setJobNumber(row.getCell(5).toString().replace(".0", ""));
//                    //读取姓名
//                    employee.setName(row.getCell(11).toString());
//                    //读取部门
//                    employee.setDepartment(row.getCell(23).toString());
                    //设置日期范围
                    employee.setDateRange(attendanceDate);

                    /** 开始处理工时信息 **/
                    List<EmployeeHours> employeeHoursList = new ArrayList<>();

                    //记录截止行数
                    int stopRowNum = sheet.getLastRowNum() + 1;
                    //计算截止行数
                    for (int j = 1; i + j < sheet.getLastRowNum() + 1; j++) {
                        Row workRow = sheet.getRow(i + j);
                        //判断到下一个人记录行数并停止
                        if (isNewEmployeeRow(workRow)) {
//                        if ("工号：".equals(workRow.getCell(4).toString())) {
                            stopRowNum = j - 2;
                            break;
                        }
                    }

                    System.out.println("工号：" + employee.getJobNumber() + ",stopRowNum:" + stopRowNum);
                    Row workRow = sheet.getRow(i + 1);

                    //读取日期行，并创建工时
                    for (int k = 0; k < workRow.getLastCellNum() + 1; k++) {

                        if (workRow.getCell(k) == null || StringUtils.isEmpty(workRow.getCell(k).toString())) {
                            continue;
                        }
                        //开始计算工时
                        EmployeeHours employeeHours = new EmployeeHours();
                        //记录日期（根据）
                        employeeHours.setDate(workRow.getCell(k).toString());

                        //读取工时数组
                        List<String> hours = new ArrayList<>();
                        for (int j = 1; j <= stopRowNum; j++) {
                            Row hoursRow = sheet.getRow(i + j + 1);
                            String hourStr = hoursRow.getCell(k).toString();
                            if (StringUtils.isEmpty(hourStr)) {
                                break;
                            }
                            String[] hourStrs = hourStr.split("\n");
                            if (hourStrs.length > 0) {
                                hours.addAll(Arrays.asList(hourStrs));
                            }
                        }

                        //处理时间数组，扣除无效数据
                        for (int j = 0; j < hours.size(); j++) {
                            String hour = hours.get(j);
                            //过滤掉无效数据
                            if ("".equals(hour.trim())) {
                                hours.remove(j);
                            }
                        }

                        System.out.println("工号：" + employee.getJobNumber() + ",employeeHours:" + JSON.toJSON(employeeHours));

                        System.out.println("工号：" + employee.getJobNumber() + ",hours:" + JSON.toJSON(hours));

                        //打断点使用
                        if ("16".equals(employee.getJobNumber())) {
                            System.out.println("断点工号：" + employee.getJobNumber());
                        }

                        /** 计算工时 **/
                        calculateWorkHours(employeeHours, hours);

                        /** 计算加班时间 **/
                        calculateWorkOvertimeHours(employeeHours, hours, employee.getDepartment());
                        //加入队列
                        employeeHoursList.add(employeeHours);
                    }

                    employee.setEmployeeHoursList(employeeHoursList);

                }

                //如果到最后一行，则将员工数据加入队列
                if (i == sheet.getLastRowNum()) {
                    employeeList.add(employee);
                }


            }

            /** 处理准备导出的数据 */
            List<EmployeeSalaryDetails> employeeSalaryDetailsList = new ArrayList<>();
            for (Employee temp : employeeList) {
                for (EmployeeHours employeeHours : temp.getEmployeeHoursList()) {
                    EmployeeSalaryDetails employeeSalaryDetails = new EmployeeSalaryDetails();
                    BeanUtil.copyProperties(temp, employeeSalaryDetails);
                    BeanUtil.copyProperties(employeeHours, employeeSalaryDetails);
                    if (employeeSalaryDetails.getWorkHours() != 0f) {
                        System.out.println(employeeSalaryDetails.getWorkHours());
                        BigDecimal bigDecimal = new BigDecimal(employeeSalaryDetails.getWorkHours());
                        employeeSalaryDetails.setWorkDays(bigDecimal.divide(new BigDecimal(9), 3, BigDecimal.ROUND_HALF_UP).toString());
                    }
                    employeeSalaryDetailsList.add(employeeSalaryDetails);
                }
            }

            //导出数据
            logger.info("开始准备导出数据:{}", JSON.toJSON(employeeSalaryDetailsList));

            // 转换

            final Integer limitPageSize = 65535;
            if (employeeList != null && employeeList.size() > limitPageSize) {
                //todo 超过最大行数应该多sheet处理
                logger.error("ExcelReaderServiceImpl -> method:uploadExcel 超过最大行数应该多sheet处理!");
            }

            // 通过工具类创建writer，默认创建xls格式
            ExcelWriter writer = ExcelUtil.getWriterWithSheet("员工工时统计表（" + attendanceDate + "）");
            // 合并单元格后的标题行，使用默认标题样式
            writer.merge(10, "员工工时统计表（" + attendanceDate + "）");
            writer.addHeaderAlias("date", "日期");
            writer.addHeaderAlias("jobNumber", "工号：");
            writer.addHeaderAlias("name", "姓名：");
            writer.addHeaderAlias("department", "部门：");
            writer.addHeaderAlias("workHours", "工时（小时）");
            writer.addHeaderAlias("workDays", "工时（天）");
            writer.addHeaderAlias("workOvertime", "加班时长");
            writer.addHeaderAlias("workOverSalary", "加班工资");
            writer.addHeaderAlias("deductSalary", "扣除工资(迟到/早退)");
            writer.addHeaderAlias("deductNoonSalary", "扣除工资(中午未打卡)");
            // 默认的，未添加alias的属性也会写出，如果想只写出加了别名的字段，可以调用此方法排除之
            writer.setOnlyAlias(true);
            // 一次性写出内容，使用默认样式，强制输出标题
            writer.write(employeeSalaryDetailsList, true);
            logger.info("导出会员投诉建议导出表 请求成功 ");
            return writer;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 计算中午是否打卡
     * Author LiZongBao
     * Date 2022/9/26 19:46
     *
     * @param employeeHours
     * @param hours
     * @return void
     **/
    private void calculateNoonHours(EmployeeHours employeeHours, List<String> hours) {
        int frequency = 0;
        for (String hour : hours) {
            if ((jisuan("12:00", hour) <= 0) && (jisuan("13:30", hour) >= 0)) {
                frequency++;
            }
        }
        if (frequency < 2) {
            employeeHours.setDeductNoonSalary(10);
        }
    }

    /**
     * 计算工时
     * Author LiZongBao
     * Date 2022/9/10 11:45
     * 迟到和早退的逻辑
     *
     * @param employeeHours
     * @param hours
     * @return void
     **/
    private void calculateWorkHours(EmployeeHours employeeHours, List<String> hours) {
        if (CollectionUtils.isEmpty(hours)) {
            return;
        }
        if (hours.size() == 1) {
            //如果全天只打了一次卡，则不计工时
            employeeHours.setWorkHours(0);
            return;
        }
        //判断是否有迟到情况（取第一个时间是否小于07:30）
        /**  早上不迟到的情况计算工时  **/
        if (jisuan("07:30", hours.get(0)) >= 0) {
            //判断最后一个时间是否大于18:00
            if (jisuan(hours.get(hours.size() - 1), "18:00") >= 0) {
                //如果都满足，记录全天工时 9小时
                employeeHours.setWorkHours(9);
                /** 2022-09-26：计算中午（12:00-13:30）是否打两次卡，不足两次扣10元
                 * 该算法只计算中午时长，如果员工只工作半天，则不扣除
                 * **/
                calculateNoonHours(employeeHours, hours);
                return;
            } else if (jisuan(hours.get(hours.size() - 1), "13:30") >= 0) {
                /*
                    //如果早上正常打卡，晚上小于六点大于一点半：记录下午实际工作时间(X-13:30)+上午4.5小时
                    employeeHours.setWorkHours(4.5f + parseHalfHours(jisuan(hours.get(hours.size() - 1), "13:30")));
                    return;
                */
                //最新需求（2022-09-15）：早退一个小时内10元，一个小时以上扣半天工资
                //计算早退时间
                if (jisuan(hours.get(hours.size() - 1), "17:00") >= 0) {
                    //早退一个小时内10元
                    employeeHours.setWorkHours(9);
                    employeeHours.setDeductSalary(10);
                    /** 2022-09-26：计算中午（12:00-13:30）是否打两次卡，不足两次扣10元
                     * 该算法只计算中午时长，如果员工只工作半天，则不扣除
                     * **/
                    calculateNoonHours(employeeHours, hours);
                    return;
                } else {
                    //早退一个小时以上，扣除半天工资
                    employeeHours.setWorkHours(4.5f);
                    return;
                }
            } else if (jisuan(hours.get(hours.size() - 1), "12:00") >= 0) {
                //如果早上正常打卡，12点-13:30打卡一次，则记半天工资
                employeeHours.setWorkHours(4.5f);
                return;
            } else {
                //如果仅上午打卡，则正常记录工时(X-07:30)
                employeeHours.setWorkHours(parseHalfHours(jisuan(hours.get(hours.size() - 1), "07:30")));
                return;
            }
            /**  早上迟到的情况计算工时（扣10元）  **/
        } else if ((jisuan(hours.get(0), "07:30") <= 60)) {
            //判断最后一个时间是否大于18:00
            if (jisuan(hours.get(hours.size() - 1), "18:00") >= 0) {
                //如果都满足，记录全天工时 9小时
                employeeHours.setWorkHours(9);
                employeeHours.setDeductSalary(10);
                /** 2022-09-26：计算中午（12:00-13:30）是否打两次卡，不足两次扣10元
                 * 该算法只计算中午时长，如果员工只工作半天，则不扣除
                 * **/
                calculateNoonHours(employeeHours, hours);
                return;
            } else if (jisuan(hours.get(hours.size() - 1), "13:30") >= 0) {
                /*
                //如果早上打卡，晚上小于六点大于一点半：记录下午实际工作时间(X-13:30)+上午4.5小时
                employeeHours.setWorkHours(4.5f + parseHalfHours(jisuan(hours.get(hours.size() - 1), "13:30")));
                employeeHours.setDeductSalary(10);
                return;
                */
                //最新需求（2022-09-15）：早退一个小时内10元，一个小时以上扣半天工资
                //计算早退时间
                if (jisuan(hours.get(hours.size() - 1), "17:00") >= 0) {
                    //早退一个小时内10元
                    employeeHours.setWorkHours(9);
                    employeeHours.setDeductSalary(20);
                    /** 2022-09-26：计算中午（12:00-13:30）是否打两次卡，不足两次扣10元
                     * 该算法只计算中午时长，如果员工只工作半天，则不扣除
                     * **/
                    calculateNoonHours(employeeHours, hours);
                    return;
                } else {
                    //早退一个小时以上，扣除半天工资
                    employeeHours.setWorkHours(4.5f);
                    employeeHours.setDeductSalary(10);
                    return;
                }
            } else if (jisuan(hours.get(hours.size() - 1), "12:00") >= 0) {
                //如果12点-13:30打卡一次，则记半天工资
                employeeHours.setWorkHours(4.5f);
                employeeHours.setDeductSalary(10);
                return;
            } else {
                //如果仅上午打卡，则正常记录工时(X-07:30)
                employeeHours.setWorkHours(parseHalfHours(jisuan(hours.get(hours.size() - 1), "07:30")));
                //如果实际工作时长超过0，即有收入，则扣去10元，没有收入则不扣除。
                if (employeeHours.getWorkHours() > 0) {
                    employeeHours.setDeductSalary(10);
                }
                return;
            }
            /**  早上迟到的超过一个小时情况计算工时（不按照实际工时计算，扣去半天工资（仅扣去上午），如果不足半天，工资为0）  **/
        } else if ((jisuan(hours.get(0), "07:30") > 60) && (jisuan("12:00", hours.get(0)) > 0)) {
            //判断最后一个时间是否大于18:00
            if (jisuan(hours.get(hours.size() - 1), "18:00") >= 0) {
                //如果都满足，记录全天工时 9小时 扣去半天
                employeeHours.setWorkHours(4.5f);
                return;
            } else if (jisuan(hours.get(hours.size() - 1), "13:30") >= 0) {
                /*
                //如果早上打卡，晚上小于六点大于一点半：记录下午实际工作时间(X-13:30)
                employeeHours.setWorkHours(parseHalfHours(jisuan(hours.get(hours.size() - 1), "13:30")));
                return;
*/
                //最新需求（2022-09-15）：早退一个小时内10元，一个小时以上扣半天工资
                //计算早退时间
                if (jisuan(hours.get(hours.size() - 1), "17:00") >= 0) {
                    //早退一个小时内10元
                    employeeHours.setWorkHours(4.5f);
                    employeeHours.setDeductSalary(20);
                    return;
                } else {
                    //早退一个小时以上，则迟到、早退均超过1小时，扣除全天天工资
                    employeeHours.setWorkHours(0);
                    return;
                }
            } else {
                //其他情况，则由于迟到不算工资
                employeeHours.setWorkHours(0);
                return;
            }
            /**  首次打卡就过了12点，并在12点-13:30之间的情况（为下午正常工作）  **/
        } else if ((jisuan(hours.get(0), "12:00") > 0) && (jisuan("13:30", hours.get(0)) > 0)) {
            //判断最后一个时间是否大于18:00
            if (jisuan(hours.get(hours.size() - 1), "18:00") >= 0) {
                //如果下午班正常，记录半天工时 4.5小时
                employeeHours.setWorkHours(4.5f);
                return;
            } else if (jisuan(hours.get(hours.size() - 1), "13:30") >= 0) {
                /*
                //如果下午班提前下班，晚上小于六点大于一点半：记录下午实际工作时间(X-13:30)
                employeeHours.setWorkHours(parseHalfHours(jisuan(hours.get(hours.size() - 1), "13:30")));
                return;
                */
                //最新需求（2022-09-15）：早退一个小时内10元，一个小时以上扣半天工资
                //计算早退时间
                if (jisuan(hours.get(hours.size() - 1), "17:00") >= 0) {
                    //早退一个小时内10元
                    employeeHours.setWorkHours(4.5f);
                    employeeHours.setDeductSalary(10);
                    return;
                } else {
                    //早退一个小时以上，扣除半天工资
                    employeeHours.setWorkHours(0);
                    return;
                }
            } else {
                //其他情况，则不算工资
                employeeHours.setWorkHours(0);
                return;
            }
            /**  首次打卡就过了13:30，并在13:30-18:00之间的情况（只记录正常工时，不考虑迟到情况，不扣工资）  **/
        } else if ((jisuan(hours.get(0), "13:30") > 0) && (jisuan("18:00", hours.get(0)) >= 0)) {
            //判断最后一个时间是否大于18:00
            if (jisuan(hours.get(hours.size() - 1), "18:00") >= 0) {
                //首次打卡13:30以后，下班打卡超过18：00，只记录正常工时：18：00减去首次打卡时长。每半小时记
                employeeHours.setWorkHours(parseHalfHours(jisuan("18:00", hours.get(0))));
                return;
            } else if (jisuan(hours.get(hours.size() - 1), "13:30") >= 0) {
                //首次打卡13:30以后，下午提前下班，只记录正常工时：最后一次打卡时间减去首次打卡时长。每半小时记
                employeeHours.setWorkHours(parseHalfHours(jisuan(hours.get(hours.size() - 1), hours.get(0))));
                return;
            } else {
                //其他情况，则不算工资
                employeeHours.setWorkHours(0);
                return;
            }
        } else {
            //其他情况，则不算工资
            employeeHours.setWorkHours(0);
            return;
        }
    }

    /**
     * 计算加班时间
     * 普通加班一个小时33
     * 其他工种一个小时50
     * Author LiZongBao
     * Date 2022/9/10 11:45
     *
     * @param employeeHours
     * @param hours
     * @return void
     **/
    private void calculateWorkOvertimeHours(EmployeeHours employeeHours, List<String> hours, String department) {
        if (CollectionUtils.isEmpty(hours)) {
            return;
        }
        if (hours.size() == 1) {
            //如果全天只打了一次卡，则不计工时
            employeeHours.setWorkOvertime(0);
            return;
        }
        //计算加班工时
        if (jisuan(hours.get(hours.size() - 1), "18:00") > 0) {
            employeeHours.setWorkOvertime(parseWholeHours(jisuan(hours.get(hours.size() - 1), "18:00")));
            int minue = jisuan(hours.get(hours.size() - 1), "18:00");
            if ("普工".equals(department)) {
                //计算普工加班工资： 每小时33元
                int salary = minue / 60 * 33;
                employeeHours.setWorkOverSalary(salary);
            } else {
                //计算其他工种加班工资： 每小时50元
                int salary = minue / 60 * 50;
                employeeHours.setWorkOverSalary(salary);
            }
        }
    }

    /**
     * 计算分钟差
     * Author LiZongBao
     * Date 2022/9/10 12:09
     *
     * @param str1 时间1
     * @param str2 时间2
     * @return 时间1-时间2 的分钟数
     **/
    private int jisuan(String str1, String str2) {
        DateFormat df = new SimpleDateFormat("HH:mm");
        try {
            Date date1 = df.parse(str1);
            Date date2 = df.parse(str2);
            Long time = date1.getTime();
            Long time2 = date2.getTime();
            int minute = (int) ((time - time2) / (60 * 1000));
            return minute;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 分钟数转半小时计数（每过半个小时记0.5）
     * Author LiZongBao
     * Date 2022/9/10 18:30
     *
     * @param minutes
     * @return float
     **/
    private float parseHalfHours(int minutes) {
        int halfHours = minutes / 30;
        return halfHours * 0.5f;
    }

    /**
     * 分钟数转小时计数（每过1个小时记1）
     * Author LiZongBao
     * Date 2022/9/10 18:30
     *
     * @param minutes
     * @return float
     **/
    private int parseWholeHours(int minutes) {
        return minutes / 60;
    }

    /**
     * 判断该行是否是员工信息行
     * 员工信息如下： 工号：1  姓名：王威  部门：电工
     * Author LiZongBao
     * Date 2022/9/12 11:51
     *
     * @param row 行数据
     * @return boolean
     **/
    private boolean isNewEmployeeRow(Row row) {
        int fCellNum = row.getFirstCellNum();
        int lCellNum = row.getLastCellNum();
        System.out.println("首列数：" + fCellNum + "-尾列数：" + lCellNum);
        for (int i = fCellNum; i < lCellNum; i++) {
            if ("工号：".equals(row.getCell(i).toString())) {
                return true;
            }
            if ("姓名：".equals(row.getCell(i).toString())) {
                return true;
            }
            if ("部门：".equals(row.getCell(i).toString())) {
                return true;
            }
        }
        return false;
    }

    /**
     * getEmployeeInfoByRow
     * Author LiZongBao
     * Date 2022/9/12 12:01
     *
     * @param row
     * @return java.lang.String
     **/
    private String getEmployeeInfoByRow(Row row, String keyword) {
        int fCellNum = row.getFirstCellNum();
        int lCellNum = row.getLastCellNum();
        System.out.println("首列数：" + fCellNum + "-尾列数：" + lCellNum);
        for (int i = fCellNum; i < lCellNum - 1; i++) {
            if (keyword.equals(row.getCell(i).toString())) {
                return row.getCell(i + 1).toString();
            }
        }
        return "";
    }

    /**
     * 读取员工信息
     * Author LiZongBao
     * Date 2022/9/12 11:59
     *
     * @param
     * @return xiangyi.entity.Employee
     **/
    private Employee getEmployeeByEmployeeRow(Row row) {
        Employee employee = new Employee();
        //读取工号
        employee.setJobNumber(getEmployeeInfoByRow(row, "工号：").replace(".0", ""));
        //读取姓名
        employee.setName(getEmployeeInfoByRow(row, "姓名：").toString());
        //读取部门
        employee.setDepartment(getEmployeeInfoByRow(row, "部门：").toString());
        return employee;
    }
}
